package com.tengju.support.application.message;

import com.tengju.support.application.shared.ApplicationException;
import com.tengju.support.domain.model.alert.AlertEvent;
import com.tengju.support.domain.model.alert.AlertService;
import com.tengju.support.domain.model.message.*;
import com.tengju.support.domain.model.user.UserInfo;
import com.tengju.support.domain.service.UserService;
import com.tengju.user.domain.model.user.UserIdCode;
import com.tengju.user.domain.model.user.UserInfoRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

import static com.tengju.support.application.shared.ApplicationExceptionCode.*;

/**
 * @author: 李大仙
 * @date: 2021/6/7 2:31 下午
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class MessageService {

    private final MessageTaskRepository messageTaskRepository;
    private final MessageRepository messageRepository;
    private final UserService userService;
    private final MessageBatchPushRelationService messageBatchPushRelationService;
    private final UserInfoRepository userInfoRepository;
    private final ApplicationContext applicationContext;
    private final AlertService alertService;

    public void add(String title,
                    MessageChannel messageChannel,
                    Integer messageType,
                    Integer messageContentType,
                    String imgUrl,
                    String content,
                    String jumpUrl,
                    Integer orientationTypeVal,
                    List<Long> orientationObjectList,
                    boolean push,
                    LocalDateTime sendTime,
                    String operator) {
        checkUser(OrientationType.fromVal(orientationTypeVal), orientationObjectList);
        MessageTask messageContent = MessageTaskFactory.create(
                title,
                messageChannel,
                messageType,
                messageContentType,
                imgUrl,
                content,
                jumpUrl,
                orientationTypeVal,
                orientationObjectList,
                push,
                sendTime,
                operator);
        messageTaskRepository.save(messageContent);
    }

    public void edit(Long messageId,
                     String title,
                     MessageType messageType,
                     MessageContentType messageContentType,
                     String imgUrl,
                     String content,
                     String jumpUrl,
                     OrientationType orientationType,
                     List<Long> orientationObjectList,
                     boolean push,
                     LocalDateTime sendTime,
                     String operator) {
        MessageTask existMessageContent = messageTaskRepository.get(new TaskId(messageId));
        if (existMessageContent == null) {
            throw ApplicationException.error(MESSAGE_NOT_EXIST, messageId);
        }
        if (SendStatus.SENDING == existMessageContent.getSendStatus()
                || SendStatus.SEND == existMessageContent.getSendStatus()) {
            throw ApplicationException.error(SENDING_STATUS_CHANGE_FAIL);
        }
        checkUser(orientationType, orientationObjectList);
        RLock lock = messageTaskRepository.getBatchInitLock(new TaskId(messageId));
        if (lock.tryLock()) {
            try {
                existMessageContent.update(title,
                        messageType,
                        messageContentType,
                        imgUrl,
                        content,
                        jumpUrl,
                        orientationType,
                        orientationObjectList,
                        push,
                        existMessageContent.getSendStatus(),
                        sendTime,
                        operator);
                messageTaskRepository.edit(existMessageContent);
            } finally {
                if (lock.isLocked()) {
                    lock.unlock();
                }
            }
        } else {
            throw ApplicationException.error(MESSAGE_LOCK, messageId);
        }
    }

    public void remove(Long messageId, String operator) {
        MessageTask existMessageContent = messageTaskRepository.get(new TaskId(messageId));
        if (existMessageContent == null) {
            throw ApplicationException.error(MESSAGE_NOT_EXIST, messageId);
        }
        if (SendStatus.SENDING == existMessageContent.getSendStatus()
                || SendStatus.SEND == existMessageContent.getSendStatus()) {
            throw ApplicationException.error(SEND_STATUS_CHANGE_FAIL, messageId);
        }
        RLock lock = messageTaskRepository.getBatchInitLock(new TaskId(messageId));
        if (lock.tryLock()) {
            try {
                messageTaskRepository.remove(new TaskId(messageId), operator);
            } finally {
                lock.unlock();
            }
        } else {
            throw ApplicationException.error(MESSAGE_LOCK, messageId);
        }
    }

    public int getTotal(String keyword, List<MessageType> messageTypeList) {
        return messageTaskRepository.getPageTotal(keyword, messageTypeList);
    }


    public List<MessageTask> getPage(String keyword, List<MessageType> messageTypeList, Integer offset, Integer limit) {
        return messageTaskRepository.getPage(keyword, messageTypeList, offset, limit);
    }

    public void sendMessage() {
        //查询当前时间以前未发送的消息任务
        LocalDateTime now = LocalDateTime.now();
        List<MessageTask> messageTaskList =
                messageTaskRepository.getBySendStatusLessSendTime(SendStatus.NOT_SEND, now);

        if (CollectionUtils.isEmpty(messageTaskList)) {
            return;
        }

        for (MessageTask messageTask : messageTaskList) {
            try {
                MessageChannel messageChannel = MessageChannel.parseByMessageType(messageTask.getMessageType());
                if (messageChannel == null) {
                    throw ApplicationException.error(INVALID_DATA,"发送类型为空");
                }
                send(messageTask,messageChannel);

                //站内信 需要推送 进行推送
                if (MessageChannel.STATION_LETTER == messageChannel && messageTask.getPush()) {
                    //创建app推送消息任务
                    MessageTask pushMessageTask = new MessageTask(null,
                            messageTask.getTitle(),MessageChannel.APP_PUSH, MessageType.APP_PUSH, messageTask.getContentType(),
                            messageTask.getImgUrl(), messageTask.getContent(), messageTask.getJumpUrl(),
                            messageTask.getMessageUserOrientation(), messageTask.getPush(),
                            SendStatus.NOT_SEND, messageTask.getCreateBy(), messageTask.getUpdateBy(),
                            messageTask.getSendTime(), messageTask.getCreateTime(), messageTask.getUpdateTime());
                    TaskId messageId = messageTaskRepository.save(pushMessageTask);
                    pushMessageTask.generateMessageId(messageId);
                    send(pushMessageTask, MessageChannel.APP_PUSH);
                }
            } catch (Exception e) {
                log.error("消息推送失败，taskId:{}",messageTask.getTaskId().getValue(),e);
                AlertEvent alertEvent = new AlertEvent("消息推送失败");
                alertEvent.addItem("messageId", messageTask.getTaskId().getValue());
                alertEvent.addItem("异常",e);
                alertService.alert(alertEvent);
            }
        }
    }


    public void sendMessageRetry() {
        //查询状态为发送中的消息任务
        List<MessageTask> messageTaskList =
                messageTaskRepository.getBySendStatus(SendStatus.SENDING);

        for (MessageTask messageTask : messageTaskList) {
            try {
                RLock lock = messageTaskRepository.getBatchInitLock(messageTask.getTaskId());
                if (lock.tryLock()) {
                    try {
                        TaskId taskId = messageTask.getTaskId();

                        List<Message> unSendRecord =
                                messageRepository.getUnSendRecordByMessageId(taskId);
                        //未发送已清零
                        if (CollectionUtils.isEmpty(unSendRecord)) {
                            messageTaskRepository.updateSendStatus(taskId,SendStatus.SENDING,SendStatus.SEND);
                            continue;
                        }
                        MessageChannel channel = MessageChannel.parseByMessageType(messageTask.getMessageType());
                        if (channel == null) {
                            throw ApplicationException.error(INVALID_DATA,"发送类型为空，taskId:" + taskId.getValue());
                        }
                        //发送消息
                        MessageSendStrategy strategy = (MessageSendStrategy) applicationContext.getBean(channel.getStrategy());
                        boolean result = strategy.send(messageTask, unSendRecord);

                        //变更消息任务表 发送状态
                        if (result) {
                            messageTaskRepository.updateSendStatus(taskId,SendStatus.SENDING,SendStatus.SEND);
                        }
                    } finally {
                        lock.unlock();
                    }
                }
            } catch (Exception e) {
                log.error("重试消息推送失败，messageId{}",messageTask.getTaskId().getValue(),e);
                AlertEvent alertEvent = new AlertEvent("重试消息推送失败");
                alertEvent.addItem("messageId", messageTask.getTaskId().getValue());
                alertEvent.addItem("异常",e);
                alertService.alert(alertEvent);
            }

        }
    }

    public void send(MessageTask messageContent, MessageChannel sendType) {
        TaskId taskId = messageContent.getTaskId();
        RLock lock = messageTaskRepository.getBatchInitLock(taskId);
        if (lock.tryLock()) {
            try {
                //定向用户
                List<UserInfo> userInfos =
                        userService.getByUserOrientation(messageContent.getMessageUserOrientation());
                if (CollectionUtils.isEmpty(userInfos)) {
                    throw ApplicationException.error(USER_ORIENTATION_NOT_FOUND,taskId.getValue());
                }
                //生成批次号
                List<Message> messageBatchRelationList =
                        messageBatchPushRelationService.generateBatch(messageContent, userInfos, sendType);
                //发送消息
                MessageSendStrategy strategy = (MessageSendStrategy) applicationContext.getBean(sendType.getStrategy());
                boolean result = strategy.send(messageContent, messageBatchRelationList);
                //变更消息任务表 发送状态
                if (result) {
                    messageTaskRepository.updateSendStatus(taskId, SendStatus.SENDING, SendStatus.SEND);
                }

            } finally {
                if (lock.isLocked()) {
                    lock.unlock();
                }
            }
        }
    }

    public void checkUser(OrientationType orientationType, List<Long> orientationObjectList) {
        if (orientationType == null) {
            throw ApplicationException.error(MESSAGE_ORIENTATION_TYPE_EMPTY);
        }
        if (OrientationType.DESIGNATED_USER == orientationType) {
            List<UserIdCode> userIdCodes = orientationObjectList.stream()
                    .map(UserIdCode::new)
                    .collect(Collectors.toList());
            List<com.tengju.user.domain.model.user.UserInfo> userInfos = userInfoRepository.batchGetUserInfo(userIdCodes);
            if (CollectionUtils.isEmpty(userInfos)){
                throw ApplicationException.error(MESSAGE_ORIENTATION_USER_NOT_EXIST, orientationObjectList);
            }
            List<Long> userIdCodeList = userInfos.stream()
                    .map(u -> u.getIdCode().getValue())
                    .collect(Collectors.toList());
            List<Long> orientationObjectListTemp = new java.util.ArrayList<>(orientationObjectList);
            orientationObjectListTemp.removeAll(userIdCodeList);
            if (orientationObjectListTemp.size() > 0){
                throw ApplicationException.error(MESSAGE_ORIENTATION_USER_NOT_EXIST, orientationObjectListTemp);
            }
        }
    }
}
